上一篇我們已經初步完成 ResumeBase 合約,能夠提供 Resume 合約該有的狀態變數、結構等,那麼今天就繼續努力把 Resume 合約完成吧!
這個合約為我們主要操作的合約,提供外部操作合約的多種函式,那麼就先來建立合約吧!
pragma solidity ^0.5.0;
import { ResumeBase } from "./ResumeBase.sol";
contract Resume is ResumeBase {
}
有注意到這次的 import 方法跟之前寫的不一樣嗎?這樣的寫法跟 ES6 的 import 方法有異曲同工之妙!我個人比較偏好這樣的 import 方式,不過這部分就是看個人習慣,看得懂最重要~
我們在父合約有使用 constructor
來設定初始資料,那麼要怎麼樣在部署 Resume 合約時,把參數帶入 ResumeBase 的 constructor
中呢?其實很簡單:
constructor(string memory name, address account, uint8 age, Gender gender) public ResumeBase(name, account, age, gender) {}
我們前面有提到 Solidity 預設是無法回傳陣列型別的,所以要完整取得一個陣列中的所有元素,就必須要知道陣列有多少個元素,再從外部一一取出來,這表示我們需要先提供外部取得陣列長度的函式。取出 educations
、 experiences
以及 skills
很容易,因為只需要取出一層的陣列,但 courses
以及 licenses
是在 educations
裡面的,每一筆學歷資料裡面都含有這兩個陣列,所以需要指定是哪一筆學歷資料,才能夠取得 courses
與 licenses
的陣列長度,並要用 IndexValidator
來確認給定的 index 是不是合法的:
function getEducationCount() public view returns(uint) {
return educations.length;
}
function getExperienceCount() public view returns(uint) {
return experiences.length;
}
function getSkillCount() public view returns(uint) {
return skills.length;
}
function getCourseCount(uint index) public view IndexValidator(index, getEducationCount()) returns(uint) {
return educations[index].courses.length;
}
function getLicenseCount(uint index) public view IndexValidator(index, getEducationCount()) returns(uint) {
return educations[index].licenses.length;
}
能夠取得陣列長度後,在 DApp 裡面就可以知道資料筆數進而用迴圈去取得資料,那麼要怎麼樣取得陣列中的資料呢?答案就是繼續寫函式!(被打)。不過在寫的時候需要注意, Solidity 不能夠回傳陣列, 也不能回傳結構 ,所以在回傳時需要個別回傳:
function getEducation(uint index) public view IndexValidator(index, getEducationCount())
returns(string memory, EducationStatus, string memory) {
Education memory edu = educations[index];
return(edu.school.name, edu.status, edu.major);
}
function getExperience(uint index) public view IndexValidator(index, getExperienceCount())
returns(string memory, string memory, uint, uint) {
Job memory exp = experiences[index];
return(exp.company.name, exp.position, exp.startDate, exp.endDate);
}
function getSkill(uint index) public view IndexValidator(index, getSkillCount())
returns(string memory, string memory) {
Skill memory skill = skills[index];
return(skill.class, skill.name);
}
function getCourse(uint eduIndex, uint index) public view
IndexValidator(eduIndex, getEducationCount())
IndexValidator(index, getCourseCount(eduIndex))
returns(string memory, string memory, string memory, uint8) {
Course memory course = educations[eduIndex].courses[index];
return(course.name, course.content, course.comment, course.grade);
}
function getLicense(uint eduIndex, uint index) public view
IndexValidator(eduIndex, getEducationCount())
IndexValidator(index, getCourseCount(eduIndex))
returns(string memory, string memory) {
License memory license = educations[eduIndex].licenses[index];
return(license.name, license.content);
}
接著來寫比較複雜的 設置 用的函式,因為設置用的函式會改變狀態變數,所以會發起交易,也就表示需要挖礦時間,故用 event
寫 log ,好讓 DApp 可以監聽交易進度並在交易上留下操作紀錄。第一個就是最基礎的,由政府設置機構單位權限的函式:
function setPermission(address account, string memory name, OrganizationType property, bool permission) public onlyGov {
organizations[account] = Organization({
name: name,
property: property,
account: account,
permission: permission
});
emit done(DoneCode.setPermission, "Set Permission");
}
設置工作經歷與離職日的函式:
function setExperience(string memory position, uint startDate) public onlyCompany {
Job memory info = Job({
company: organizations[msg.sender],
position: position,
startDate: startDate,
endDate: 0
});
experiences.push(info);
emit done(DoneCode.setExperience, "Set Experience");
}
function setJobEndDate(uint endDate) public onlyCompany {
uint index = uint(findOrganization(msg.sender, "experience"));
experiences[index].endDate = endDate;
emit done(DoneCode.setJobEndDate, "Set JobEndDate");
}
設置學歷的方式較為特別,因為學歷的結構中還有其他結構的陣列,而且是無法忽略的,表示在新增的同時就需要帶入 courses
與 licenses
,然而 Solidity 無法在 memory
的結構變數中利用 push
新增結構中的結構陣列(嗯...好繞口令XD),簡單來說就是我們不能先宣告一個 memory
的 Education
變數並用 push
的方式新增 courses
與 licenses
,需要有一個 storage
的 Education
才能夠 push
進去,但今天我們 educations
本身就是該被新增元素的陣列,不可能做一個狀態變數專門處理這種事情,一定是要用 educations
這個陣列來處理,那我們要怎樣才能這麼做呢?其實我們可以先擴充 educations
的長度,然後用擴充的這個來做處理,這也是 Solidity 留的後路(啊不就還好動態陣列可以這樣擴充):
function setEducation(EducationStatus status, string memory major) public onlySchool {
educations.length++;
Education storage edu = educations[educations.length - 1];
Course memory course = Course({ name: "", content: "", comment: "", grade: 0 });
License memory license = License({ name: "", content: "" });
edu.school = organizations[msg.sender];
edu.status = status;
edu.major = major;
edu.courses.push(course);
edu.licenses.push(license);
emit done(DoneCode.setEducation, "Set Education");
}
接著就是設置專業證照與修課證明,會需要先找到該教育單位位於陣列的哪個位置,再針對該元素更新:
function setLicense(string memory name, string memory content) public onlySchool {
uint index = uint(findOrganization(msg.sender, "education"));
Education storage edu = educations[index];
edu.licenses.push(License({ name: name, content: content }));
emit done(DoneCode.setLicense, "Set License");
}
function setCourse(string memory name, string memory content, string memory comment, uint8 grade) public onlySchool {
uint index = uint(findOrganization(msg.sender, "education"));
Education storage edu = educations[index];
edu.courses.push(Course({ name: name, content: content, comment: comment, grade: grade }));
emit done(DoneCode.setCourse, "Set Course");
}
還有設置自傳、專業技術與聯絡方式的函式:
function setAutobiography(string memory text) public onlyHost {
profile.autobiography = text;
emit done(DoneCode.setAutobiography, "Set Autobiography");
}
function setSkill(string memory class, string memory name) public onlyHost {
skills.push(Skill({ class: class, name: name }));
emit done(DoneCode.setSkill, "Set Skill");
}
function setContact(string memory contact) public onlyHost {
profile.contact = contact;
emit done(DoneCode.setContact, "Set Contact");
}
移除 的部分就是移除權限與專業技能:
function removePermission(address account) public onlyGov {
Organization storage org = organizations[account];
org.permission = false;
emit done(DoneCode.removePermission, "Remove Permission");
}
function removeSkill(string memory class, string memory name) public onlyHost {
uint index = 0;
for (uint i = 0; i < skills.length; i++) {
if (skills[i].name.compare(name) && skills[i].class.compare(class)) {
index = i;
break;
}
}
for (uint i = index; index < skills.length - 1; i++) {
skills[i] = skills[i + 1];
}
delete skills[skills.length - 1];
skills.length--;
emit done(DoneCode.removeSkill, "Remove Skill");
}
恭喜完成簡單的履歷合約了!
今天完成了履歷智能合約,預計明天的內容會是做驗證,實際操作一次讓各位更了解這份智能合約要如何使用!
請問"emit done(DoneCode.removeSkill, "Remove Skill");"這句是否會立刻寫入event log,還是將Remove Skill字串儲存DoneCode裏的removeSkill裏,之後才寫入event log?
你好,DoneCode 只是 enum
,並不會有任何資料被寫入 enum
,另外,event log 是在交易處理完後才能夠查詢到。